library ieee;
use ieee.std_logic_1164.all;

-- This arbitrary state machine is probably not very useful in reality, but demonstrates 

entity data_check is
    port (clock,reset         : in  std_logic;
          data                : in  std_logic_vector(7 downto 0);
          ack                 : in  std_logic;
          frame_len           : in  std_logic_vector(10 downto 0);
          port_num            : in  std_logic_vector(1 downto 0);
          data_err            : out std_logic;
          port_err            : out std_logic;
          len_err             : out std_logic;
          len_out             : out std_logic_vector(10 downto 0);
          frame_count         : out std_logic_vector(10 downto 0)
    );
end data_check;

architecture behavior of data_check is

type   state_type is (wait_state, receive_state);
signal my_state_reg, my_state_next: state_type := wait_state;

signal q,ff_data              : std_logic_vector(7 downto 0);
signal eq                     : std_logic := '0';
signal frame_count_en         : std_logic;
signal count                  : std_logic_vector(10 downto 0);
signal load                   : std_logic;
signal count_reset            : std_logic;
signal countdown_en           : std_logic;
signal load_len               : std_logic;
signal len_remaining          : std_logic_vector(10 downto 0);
signal acked                  : std_logic;
signal ack_ff_en              : std_logic;
signal ack_ff_clr             : std_logic;

component prbg is
    port (clock,reset         : in  std_logic;
          clock_en			  : in  std_logic;
          seed                : in  std_logic_vector(7 downto 0);
          load                : in  std_logic;
          prb                 : out std_logic_vector(7 downto 0)
    );
end component;

component compare is
	port
	(
		dataa		: IN STD_LOGIC_VECTOR (7 DOWNTO 0);
		datab		: IN STD_LOGIC_VECTOR (7 DOWNTO 0);
		AeB		    : OUT STD_LOGIC 
	);
end component;

component countdown is
	port
	(
		aclr		: in  std_logic;
		clock		: in  std_logic;
		cnt_en		: in  std_logic;
		data		: in  std_logic_vector(10 downto 0);
		sload		: in  std_logic;
		q			: out std_logic_vector(10 downto 0)
	);
end component;

component fwd_test_countup is
	port
	(
		aclr		: IN STD_LOGIC ;
		clock		: IN STD_LOGIC ;
		cnt_en		: IN STD_LOGIC ;
		q		    : OUT STD_LOGIC_VECTOR (10 DOWNTO 0)
	);
end component;

component fwd_test_ff8 is
	port
	(
		aclr		: IN STD_LOGIC ;
		clock		: IN STD_LOGIC ;
		data		: IN STD_LOGIC_VECTOR (7 DOWNTO 0);
		enable		: IN STD_LOGIC ;
		q		    : OUT STD_LOGIC_VECTOR (7 DOWNTO 0)
	);
end component;

component fwd_test_ff1 is
	port
	(
		aclr		: IN STD_LOGIC ;
		clock		: IN STD_LOGIC ;
		data		: IN STD_LOGIC ;
		q		: OUT STD_LOGIC 
	);
end component;

begin

process(clock, reset)
	begin
		if(reset ='1') then 
		  my_state_reg <= wait_state;
		elsif(clock'event and clock='1') then
			my_state_reg<=my_state_next;
		end if;
	end process;

process(my_state_reg, data, ack) 
	begin
	    case my_state_reg is
		    when wait_state => 
		        if (data = "00000000") then
		            my_state_next <= wait_state;
		        else
		            my_state_next <= receive_state;
		        end if;
			when receive_state =>
			    if (data = "00000000") then
					my_state_next <= wait_state;
				else
					my_state_next <= receive_state;
				end if;
		end case;
	end process;

process (my_state_reg,data,count,port_num,len_remaining,eq,ff_data,acked)
	begin
	    len_err <= '0';
        port_err <= '0';
        data_err <= '0';
        
        load <= '0';
        count_reset <= '1';
        frame_count_en <= '0';
        countdown_en <= '0';
        load_len <= '0';
        ack_ff_en <= '0';
        ack_ff_clr <= '0';
        
		case my_state_reg is
			when wait_state => 
				if (data = "00000000") then
		            load <= '0';
		            ack_ff_clr <= '1';
		        else
		            load <= '1';
		            load_len <= '1';
		            count_reset <= '0';
		            countdown_en <= '1';
		            ack_ff_en <= '1';
		        end if;
		        if not (len_remaining = "00000000000") then
		            len_err <= '1';
		        end if;
			when receive_state => 
			    count_reset <= '0';
			    countdown_en <= '1';
			    data_err <= not eq;
			    if (count = "00000000110") then
		            countdown_en <= '0';
		            if not ((ff_data(1 downto 0) = port_num(1 downto 0)) or (ff_data(2) = '1')) then
		                port_err <= acked;
		            else
		                port_err <= not acked;
		            end if;
		            if ((ff_data(2) = '0' or port_num = "00") and acked = '1') then
		                frame_count_en <= '1';
		            end if;
		        end if;
		        --if (ff_data = "00000000") then
		            --if not (len_remaining = "00000000000") then
		            --    len_err <= '1';
		            --end if;
		        --end if;
		end case;
	end process;

shift: prbg port map(clock,reset,'1',data,load,q);
comp: compare port map(ff_data,q,eq);
counter: fwd_test_countup port map(reset or count_reset,clock,'1',count);
frame_counter: fwd_test_countup port map(reset,clock,frame_count_en,frame_count);
ff: fwd_test_ff8 port map(reset,clock,data,'1',ff_data);
ff2: fwd_test_ff1 port map(reset or ack_ff_clr,clock,ack or acked,acked);
len_count: countdown port map(reset,clock,countdown_en,frame_len,load_len,len_remaining);

len_out <= len_remaining;

end behavior;